package handle

import (
	"bytes"
	"crypto/tls"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"MPCBot/global"
	"MPCBot/handle/hdwallet"
	"MPCBot/model"
	"MPCBot/utils/compute/pub/keeper"
	"MPCBot/utils/compute/pub/types"
	"MPCBot/utils/config"

	"github.com/CosmWasm/wasmd/x/wasm"
	hermitcli "github.com/HermitMatrixNetwork/HermitMatrixNetwork/x/compute/client/cli"
	wasmUtils "github.com/HermitMatrixNetwork/HermitMatrixNetwork/x/compute/client/utils"
	cosmosclient "github.com/cosmos/cosmos-sdk/client"
	cosmostx "github.com/cosmos/cosmos-sdk/client/tx"
	"github.com/cosmos/cosmos-sdk/codec"
	codetype "github.com/cosmos/cosmos-sdk/codec/types"
	cosmoscrypto "github.com/cosmos/cosmos-sdk/crypto"
	"github.com/cosmos/cosmos-sdk/crypto/keyring"
	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/cosmos/cosmos-sdk/types/tx/signing"
	xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
	"github.com/cosmos/cosmos-sdk/x/auth/tx"
	authtype "github.com/cosmos/cosmos-sdk/x/auth/types"
	bank "github.com/cosmos/cosmos-sdk/x/bank/types"
	"github.com/cosmos/ibc-go/v3/testing/simapp/params"
	"github.com/evmos/ethermint/app"
	"github.com/evmos/ethermint/crypto/hd"
	enccodec "github.com/evmos/ethermint/encoding/codec"
	flag "github.com/spf13/pflag"
)

type Account struct {
	keyStorePath       string
	keyOwnerName       string
	keyOwnerPw         string
	fullFundraiserPath string
	selectAlgorithm    string
	mnemonic           string
	Address            string
	priKey             string
	keyring            keyring.Keyring
	keyInfo            keyring.Info
}

type HermitClient struct {
	ChainID             string
	NetWorkInfo         *config.NetWorkInfo
	ClientCtx           cosmosclient.Context
	ExecAccount         *Account
	GasPrice            string
	GasLimit            string
	SwapGasAmount       string
	SwapGasAmountString string
}

type ExecParam struct {
	ExecuteTask struct {
		UserAdr string `json:"user_adr"`
	} `json:"add_bank_list"`
}

// init hermitClient
func (_hermitClient *HermitClient) Init(password string, mnemonic string) {
	chainID := global.GetNewWorkInfo(model.CosmosChainID).Name
	_hermitClient.GasLimit = "5000000"
	_hermitClient.GasPrice = "70000"
	_hermitClient.SwapGasAmount = "0"
	_hermitClient.SwapGasAmountString = "0ughm"
	_hermitClient.ChainID = chainID
	_hermitClient.NetWorkInfo = global.GetNewWorkInfo(model.CosmosChainID)

	// init execaccount
	_hermitClient.ExecAccount = &Account{
		keyStorePath:       "./",
		keyOwnerName:       "wasm",
		keyOwnerPw:         password,
		fullFundraiserPath: "m/44'/118/0'/0/0",
		selectAlgorithm:    "Secp256k1",
		mnemonic:           mnemonic, // "tag volcano eight thank tide danger coast health above argue embrace heavy"
	}

	// init config
	SetChainPrefixConfig()
	encodingConfig := MakeEncodingConfigEth()

	// init client
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithChainID(chainID)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithNodeURI(_hermitClient.NetWorkInfo.RpcUrls[0])
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithKeyringDir(_hermitClient.ExecAccount.keyStorePath)
	node, err := cosmosclient.NewClientFromNode(global.GetNewWorkInfo(model.CosmosChainID).RpcUrls[0])
	if err != nil {
		global.Log.Error("====>  NewClientFromNode error： %s", err.Error())
	}
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithClient(node)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithTxConfig(encodingConfig.TxConfig)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithCodec(encodingConfig.Marshaler)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithLegacyAmino(encodingConfig.Amino)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithInterfaceRegistry(encodingConfig.InterfaceRegistry)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithKeyringOptions(hd.EthSecp256k1Option())
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithGenerateOnly(false)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithBroadcastMode("block")
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithAccountRetriever(authtype.AccountRetriever{})

	// init wallet
	_hermitClient.ExecAccount.keyring, _hermitClient.ExecAccount.keyInfo, _, _hermitClient.ExecAccount.priKey = hdwallet.UserNewKey(_hermitClient.ExecAccount.mnemonic, _hermitClient.ExecAccount.keyStorePath, _hermitClient.ExecAccount.keyOwnerName, _hermitClient.ExecAccount.keyOwnerPw, _hermitClient.ExecAccount.fullFundraiserPath, _hermitClient.ExecAccount.selectAlgorithm)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithKeyring(_hermitClient.ExecAccount.keyring)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithFromAddress(_hermitClient.ExecAccount.keyInfo.GetAddress()).WithFromName(_hermitClient.ExecAccount.keyInfo.GetName())
	priv, _, err := cosmoscrypto.UnarmorDecryptPrivKey(_hermitClient.ExecAccount.priKey, _hermitClient.ExecAccount.keyOwnerPw)
	if err != nil {
		global.Log.Error("====>  UnarmorDecryptPrivKey error： %s", err.Error())
	}
	gwAdd, err := sdk.AccAddressFromHex(priv.PubKey().Address().String())
	if err != nil {
		global.Log.Error("====>  AccAddressFromHex error： %s", err.Error())
	}
	_hermitClient.ClientCtx.FromAddress = gwAdd
	// global.Log.Error("====>  _hermitClient keyInfo.priket %v", _hermitClient.ExecAccount.priKey)
	// global.Log.Error("====>  ExecCosmosTr priv %v", priv.String())
	global.Log.Warn("====>  tr sender %s", gwAdd.String())
}

func (_hermitClient *HermitClient) ExecuteTask(userAdr string, tokenAdr string) (trHash string, result bool) {
	var err error
	var execParamJson []byte
	var execParam ExecParam
	execParam.ExecuteTask.UserAdr = userAdr
	execParamJson, err = json.Marshal(execParam)
	if err != nil {
		fmt.Println("Umarshal failed:", err)
		return
	}

	res, trHash := hermitClient.ExecCosmosTr(tokenAdr, string(execParamJson))

	return trHash, res
}

func (_hermitClient *HermitClient) ExecCosmosTr(executeContract string, execParam string) (result bool, trHash string) {

	global.Log.Warn("====>  ExecCosmosTr executeContract %v", executeContract)
	global.Log.Warn("====>  ExecCosmosTr execParam %v", execParam)

	gasFalg := flag.NewFlagSet(_hermitClient.SwapGasAmount, flag.ContinueOnError)
	gasFalg.String(model.FlagAmount, _hermitClient.SwapGasAmountString, "Amount of coins to bond")
	contractAddress, err := sdk.AccAddressFromBech32(executeContract)
	if err != nil {
		return
	}
	msg, err := ParseExecuteArgs(_hermitClient.ClientCtx, contractAddress, execParam, _hermitClient.ClientCtx.GetFromAddress(), gasFalg)
	if err != nil {
		global.Log.Error("====>  ExecCosmosTr error： %s", err.Error())
		return false, err.Error()
	}
	priv, _, err := cosmoscrypto.UnarmorDecryptPrivKey(_hermitClient.ExecAccount.priKey, _hermitClient.ExecAccount.keyOwnerPw)
	if err != nil {
		global.Log.Error("====>  ExecCosmosTr error： %s", err.Error())
	}
	res := _hermitClient.TxCreate(priv, msg, "execute")
	global.Log.Warn("====>  ExecCosmosTr res %v", res)
	// _hermitClient.decryptFailHash(res.TxHash)

	return res.Code == 0, res.TxHash
}

func (_hermitClient *HermitClient) TxCreate(priv cryptotypes.PrivKey, msg interface{}, option string) *sdk.TxResponse {

	// var response cns.HttpResponseStruct
	builder := _hermitClient.ClientCtx.TxConfig.NewTxBuilder()

	//-- Setting message type after transaction type check
	if option == model.Bank {
		convertMsg, _ := msg.(*bank.MsgSend)
		builder.SetMsgs(convertMsg)

	} else if option == model.Store {
		convertMsg, _ := msg.(wasm.MsgStoreCode)
		builder.SetMsgs(&convertMsg)

	} else if option == model.Instantiate {
		convertMsg, _ := msg.(types.MsgInstantiateContract)
		builder.SetMsgs(&convertMsg)

	} else if option == model.Execute {
		convertMsg, _ := msg.(types.MsgExecuteContract)
		builder.SetMsgs(&convertMsg)
	}

	gasLimitu64 := FromStringToUint64(_hermitClient.GasLimit)
	builder.SetGasLimit(gasLimitu64)

	//-- feeAmount
	feeAmount := sdk.Coin{
		Amount: sdk.NewInt(FromStringToInt64(_hermitClient.GasPrice)),
		Denom:  _hermitClient.NetWorkInfo.MainCoinName,
	}
	feeAmountCoins := sdk.NewCoins(feeAmount)
	builder.SetFeeAmount(feeAmountCoins)

	// -- If using multisig, input privs, accNums and accSeqs of other accounts
	privs := []cryptotypes.PrivKey{priv}
	gwAdd, err := sdk.AccAddressFromHex(priv.PubKey().Address().String())
	if err != nil {
		global.Log.Error("====>  TxCreate AccAddressFromHex error： %s", err.Error())
		return nil
	}
	//-- Get GW address's account number and sequence
	accNum, accSeq, err := GetAccountInfoHttpClient(gwAdd.String())
	if err != nil {
		global.Log.Error("====>  TxCreate GetAccountInfoHttpClient error： %s", err.Error())
		return nil
	}
	accNums := []uint64{FromStringToUint64(accNum)}
	accSeqs := []uint64{FromStringToUint64(accSeq)}

	var sigsV2 []signing.SignatureV2
	_, err = txSignRound(sigsV2, _hermitClient.ChainID, _hermitClient.ClientCtx, privs, accSeqs, accNums, builder)
	if err != nil {
		global.Log.Error("====>  TxCreate txSignRound error： %s", err.Error())
	}

	txBytes, err := _hermitClient.ClientCtx.TxConfig.TxEncoder()(builder.GetTx())
	if err != nil {
		global.Log.Error("====>  TxCreate txBytes error： %s", err.Error())
	}

	res, err := _hermitClient.ClientCtx.BroadcastTx(txBytes)
	if err != nil {
		global.Log.Error("====>  TxCreate BroadcastTx error： %s", err.Error())
	}

	return res
}

func (_hermitClient *HermitClient) decryptFailHash(hash string) {
	dataCipherBz, err := base64.StdEncoding.DecodeString(hash)
	if err != nil {
		global.Log.Error("error while trying to decode the encrypted output data from base64: %w", err)
	}

	nonce := dataCipherBz[0:32]
	originalTxSenderPubkey := dataCipherBz[32:64]

	wasmCtx := wasmUtils.WASMContext{CLIContext: _hermitClient.ClientCtx}
	_, myPubkey, err := wasmCtx.GetTxSenderKeyPair()
	if err != nil {
		global.Log.Error("error GetTxSenderKeyPair: %w", err)
	}
	if !bytes.Equal(originalTxSenderPubkey, myPubkey) {
		global.Log.Error("cannot decrypt, not original tx sender")
	}

	dataPlaintextB64Bz, err := wasmCtx.Decrypt(dataCipherBz[64:], nonce)
	if err != nil {
		global.Log.Error("error while trying to decrypt the output data: %w", err)
	}

	fmt.Printf("Decrypted data: %s", dataPlaintextB64Bz)
}

func (_hermitClient *HermitClient) ExportAccountPrivate(mnemonic string) {
	global.Log.Error("====>  mnemonic %s", mnemonic)
	chainID := global.GetNewWorkInfo(model.CosmosChainID).Name
	_hermitClient.GasLimit = "5000000"
	_hermitClient.GasPrice = "100000"
	_hermitClient.SwapGasAmount = "0"
	_hermitClient.SwapGasAmountString = "0ughm"
	_hermitClient.ChainID = chainID
	_hermitClient.NetWorkInfo = global.GetNewWorkInfo(model.CosmosChainID)

	// init execaccount
	_hermitClient.ExecAccount = &Account{
		keyStorePath:       "./",
		keyOwnerName:       "wasm",
		keyOwnerPw:         "hhhhjjjj",
		fullFundraiserPath: "m/44'/928'/0'/0/0",
		selectAlgorithm:    "Secp256k1",
		mnemonic:           mnemonic,
	}

	// init config
	SetChainPrefixConfig()
	encodingConfig := MakeEncodingConfigEth()

	// init client
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithChainID(chainID)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithNodeURI(_hermitClient.NetWorkInfo.RpcUrls[0])
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithKeyringDir(_hermitClient.ExecAccount.keyStorePath)
	node, err := cosmosclient.NewClientFromNode(global.GetNewWorkInfo(model.CosmosChainID).RpcUrls[0])
	if err != nil {
		global.Log.Error("====>  NewClientFromNode error： %s", err.Error())
	}
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithClient(node)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithTxConfig(encodingConfig.TxConfig)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithCodec(encodingConfig.Marshaler)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithLegacyAmino(encodingConfig.Amino)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithInterfaceRegistry(encodingConfig.InterfaceRegistry)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithKeyringOptions(hd.EthSecp256k1Option())
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithGenerateOnly(false)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithBroadcastMode("block")
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithAccountRetriever(authtype.AccountRetriever{})

	// init wallet
	_hermitClient.ExecAccount.keyring, _hermitClient.ExecAccount.keyInfo, _, _hermitClient.ExecAccount.priKey = hdwallet.UserNewKey(_hermitClient.ExecAccount.mnemonic, _hermitClient.ExecAccount.keyStorePath, _hermitClient.ExecAccount.keyOwnerName, _hermitClient.ExecAccount.keyOwnerPw, _hermitClient.ExecAccount.fullFundraiserPath, _hermitClient.ExecAccount.selectAlgorithm)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithKeyring(_hermitClient.ExecAccount.keyring)
	_hermitClient.ClientCtx = _hermitClient.ClientCtx.WithFromAddress(_hermitClient.ExecAccount.keyInfo.GetAddress()).WithFromName(_hermitClient.ExecAccount.keyInfo.GetName())
	priv, _, err := cosmoscrypto.UnarmorDecryptPrivKey(_hermitClient.ExecAccount.priKey, _hermitClient.ExecAccount.keyOwnerPw)
	if err != nil {
		global.Log.Error("====>  UnarmorDecryptPrivKey error： %s", err.Error())
	}
	gwAdd, err := sdk.AccAddressFromHex(priv.PubKey().Address().String())
	if err != nil {
		global.Log.Error("====>  AccAddressFromHex error： %s", err.Error())
	}
	_hermitClient.ClientCtx.FromAddress = gwAdd
	global.Log.Error("====>  _hermitClient keyInfo.priket \n %v", _hermitClient.ExecAccount.priKey)
	global.Log.Warn("====>  account address %s", gwAdd.String())
}

func txSignRound(sigsV2 []signing.SignatureV2, chainID string, clientCtx cosmosclient.Context, privs []cryptotypes.PrivKey, accSeqs []uint64, accNums []uint64, builder cosmosclient.TxBuilder) ([]signing.SignatureV2, error) {

	for i, priv := range privs {
		sigV2 := signing.SignatureV2{
			PubKey: priv.PubKey(),
			Data: &signing.SingleSignatureData{
				SignMode:  clientCtx.TxConfig.SignModeHandler().DefaultMode(),
				Signature: nil,
			},
			Sequence: accSeqs[i],
		}
		sigsV2 = append(sigsV2, sigV2)
	}

	err := builder.SetSignatures(sigsV2...)
	if err != nil {
		global.Log.Error("====>  Testhermit error： %s", err.Error())
		return nil, err
	}

	sigsV2 = []signing.SignatureV2{}
	for i, priv := range privs {
		signerData := xauthsigning.SignerData{
			ChainID:       chainID,
			AccountNumber: accNums[i],
			Sequence:      accSeqs[i],
		}
		sigV2, err := cosmostx.SignWithPrivKey(
			clientCtx.TxConfig.SignModeHandler().DefaultMode(),
			signerData,
			builder,
			priv,
			clientCtx.TxConfig,
			accSeqs[i],
		)
		if err != nil {
			global.Log.Error("====>  Testhermit error： %s", err.Error())
			return nil, err
		}

		sigsV2 = append(sigsV2, sigV2)
	}

	err = builder.SetSignatures(sigsV2...)
	if err != nil {
		global.Log.Error("====>  Testhermit error： %s", err.Error())
		return nil, err
	}

	return sigsV2, nil
}

func ParseExecuteArgs(cliCtx cosmosclient.Context, contractAddr sdk.AccAddress, msg string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgExecuteContract, error) {
	amountStr, err := flags.GetString(model.FlagAmount)
	if err != nil {
		return types.MsgExecuteContract{}, fmt.Errorf("amount: %s", err)
	}

	coins, err := sdk.ParseCoinsNormalized(amountStr)
	if err != nil {
		return types.MsgExecuteContract{}, err
	}

	wasmCtx := wasmUtils.WASMContext{CLIContext: cliCtx}
	codeHash, err := GetCodeHashByContractAddr(cliCtx, contractAddr)
	if err != nil {
		return types.MsgExecuteContract{}, err
	}

	execMsg := types.NewSecretMsg(codeHash, []byte(msg))
	encryptedMsg, err := wasmCtx.Encrypt(execMsg.Serialize())
	if err != nil {
		return types.MsgExecuteContract{}, err
	}

	return types.MsgExecuteContract{
		Sender:           sender,
		Contract:         contractAddr,
		CallbackCodeHash: "",
		SentFunds:        coins,
		Msg:              encryptedMsg,
	}, nil
}

func GetCodeHashByContractAddr(cliCtx cosmosclient.Context, contractAddr sdk.AccAddress) ([]byte, error) {
	route := fmt.Sprintf("custom/%s/%s/%s", types.QuerierRoute, keeper.QueryContractHash, contractAddr.String())
	res, _, err := cliCtx.Query(route)
	if err != nil {
		return nil, err
	}

	return []byte(hex.EncodeToString(res)), nil
}

// -- Get account number and sequence
func HttpClient(method string, url string, body []byte) []byte {
	var request *http.Request
	var err error

	if method == "GET" {
		request, err = http.NewRequest("GET", url, nil)
		if err != nil {
			global.Log.Error("====>  Testhermit error： %s", err.Error())
		}
	} else {
		buf := bytes.NewBuffer(body)
		request, err = http.NewRequest("POST", url, buf)
		if err != nil {
			global.Log.Error("====>  Testhermit error： %s", err.Error())
		}
	}

	hClient := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
	}
	hClient.Timeout = time.Second * 30
	defer func() {
		if err := recover(); err != nil {
			global.Log.Error("====>  Testhermit error： %s", err)
		}
	}()
	response, err := hClient.Do(request)
	if err != nil {
		global.Log.Error("====>  Testhermit error： %s", err.Error())
	}
	defer response.Body.Close()

	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		global.Log.Error("====>  Testhermit error： %s", err.Error())
	}

	response.Body.Close()

	return responseBody
}

func MakeEncodingConfigEth() params.EncodingConfig {
	cdc := codec.NewLegacyAmino()
	interfaceRegistry := codetype.NewInterfaceRegistry()
	marshaler := codec.NewProtoCodec(interfaceRegistry)

	encodingConfig := params.EncodingConfig{
		InterfaceRegistry: interfaceRegistry,
		Marshaler:         marshaler,
		TxConfig:          tx.NewTxConfig(marshaler, tx.DefaultSignModes),
		Amino:             cdc,
	}

	enccodec.RegisterLegacyAminoCodec(encodingConfig.Amino)
	app.ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino)
	enccodec.RegisterInterfaces(encodingConfig.InterfaceRegistry)
	app.ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry)
	return encodingConfig
}

func SetChainPrefixConfig() {
	// Bech32MainPrefix defines the main SDK Bech32 prefix of an account's address
	var Bech32MainPrefix = "ghm"

	var (
		// PrefixValidator is the prefix for validator keys
		PrefixValidator = "val"
		// PrefixConsensus is the prefix for consensus keys
		PrefixConsensus = "cons"
		// PrefixPublic is the prefix for public keys
		PrefixPublic = "pub"
		// PrefixOperator is the prefix for operator keys
		PrefixOperator = "oper"

		// Bech32PrefixAccAddr defines the Bech32 prefix of an account's address
		Bech32PrefixAccAddr = Bech32MainPrefix
		// Bech32PrefixAccPub defines the Bech32 prefix of an account's public key
		Bech32PrefixAccPub = Bech32MainPrefix + PrefixPublic
		// Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address
		Bech32PrefixValAddr = Bech32MainPrefix + PrefixValidator + PrefixOperator
		// Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key
		Bech32PrefixValPub = Bech32MainPrefix + PrefixValidator + PrefixOperator + PrefixPublic
		// Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address
		Bech32PrefixConsAddr = Bech32MainPrefix + PrefixValidator + PrefixConsensus
		// Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key
		Bech32PrefixConsPub = Bech32MainPrefix + PrefixValidator + PrefixConsensus + PrefixPublic
	)

	config := sdk.GetConfig()
	config.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub)
	config.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub)
	config.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub)
	// config.SetCoinType(CoinType)
	// config.SetFullFundraiserPath(FullFundraiserPath)
	config.Seal()
}

func FromStringToUint64(value string) uint64 {
	number, err := strconv.ParseUint(value, 10, 64)
	if err != nil {
		global.Log.Error("====>  Testhermit error： %s", err.Error())
	}

	return number
}

func FromStringToInt64(value string) int64 {
	number, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		global.Log.Error("====>  Testhermit error： %s", err.Error())
	}

	return number
}

func GetAccountInfoHttpClient(gwKeyAddress string) (string, string, error) {
	restEndpoint := global.GetNewWorkInfo(model.CosmosChainID).RestEndpoint
	url := restEndpoint + model.UserInfoUrl + gwKeyAddress
	responseBody := HttpClient("GET", url, nil)
	global.Log.Error("====>  Account Response： %s", string(responseBody))

	//-- The account does not have any coins or tokens, not included the chain
	//-- EthAccount -> eth_secp256k1
	if strings.Contains(string(responseBody), "EthAccount") {
		return "accountNumber", "accountSequence", nil

	} else {
		//-- secp256k1
		if strings.Contains(string(responseBody), "code") {
			return "", "", errors.New("Code : ")

		} else {
			var responseStruct model.ResponseStruct
			responseData := JsonUnmarshalData(responseStruct, responseBody)
			accountNumber := responseData.(map[string]interface{})["account"].(map[string]interface{})["account_number"].(string)
			sequence := responseData.(map[string]interface{})["account"].(map[string]interface{})["sequence"].(string)
			return accountNumber, sequence, nil
		}
	}
}

func JsonUnmarshalData(jsonStruct interface{}, byteValue []byte) interface{} {
	json.Unmarshal(byteValue, &jsonStruct)
	return jsonStruct
}

const MessageBlockSize = 256

type BalanceMsg struct {
	Balance BalanceMsgInner `json:"balance"`
}

type BalanceMsgInner struct {
	Address sdk.AccAddress `json:"address"`
	Key     string         `json:"key"`
}

type TransferHistoryMsgInner struct {
	Address  sdk.AccAddress `json:"address"`
	Key      string         `json:"key"`
	Page     uint32         `json:"page"`
	PageSize uint32         `json:"page_size"`
}

type TransferHistoryMsg struct {
	TransferHistory TransferHistoryMsgInner `json:"transfer_history"`
}

func (_hermitClient *HermitClient) queryTransferHistoryMsg(tokenAdr string, viewingKey string, page uint32, pageSize uint32) error {
	fromAddress, err := sdk.AccAddressFromBech32(tokenAdr)
	if err != nil {
		return err
	}
	msg := TransferHistoryMsg{
		TransferHistory: TransferHistoryMsgInner{
			Address:  fromAddress,
			Key:      viewingKey,
			Page:     page,
			PageSize: pageSize,
		},
	}
	jsonMsg, err := json.Marshal(&msg)
	if err != nil {
		return err
	}

	queryData := []byte(spacePad(MessageBlockSize, string(jsonMsg)))
	global.Log.Error("====>  ExecCosmosTr error： %v", queryData)
	err = hermitcli.QueryWithData(fromAddress, queryData, _hermitClient.ClientCtx)
	if err != nil {
		return err
	}

	return nil
}

func (_hermitClient *HermitClient) queryBalanceMsg(fromAddress sdk.AccAddress, viewingKey string) ([]byte, error) {
	msg := BalanceMsg{
		Balance: BalanceMsgInner{
			Address: fromAddress,
			Key:     viewingKey,
		},
	}
	jsonMsg, err := json.Marshal(&msg)
	if err != nil {
		return nil, err
	}

	return []byte(spacePad(MessageBlockSize, string(jsonMsg))), nil
}

func spacePad(blockSize int, message string) string {
	surplus := len(message) % blockSize
	if surplus == 0 {
		return message
	}

	missing := blockSize - surplus
	return message + strings.Repeat(" ", missing)
}
